package HTML::FormFu::Element::Dojo::Repeatable;

use strict;
use warnings;
use base 'HTML::FormFu::Element::Block';

use HTML::FormFu::Attribute qw/ mk_attrs /;
use HTML::FormFu::ObjectUtil qw/ insert_after /;
use HTML::FormFu::Util qw/ 
    append_xml_attribute _parse_args _get_elements process_attrs
    xml_escape /;

__PACKAGE__->mk_accessors(qw/ 
    action count link_label _custom_elements repeat_id 
    count_name count_id increment_name _repeated  /);

__PACKAGE__->mk_attrs(qw/ link_attributes /);

sub new {
    my $self = shift->SUPER::new(@_);

    $self->tag('span');
    $self->_repeated([]);
    
    $self->id('default_id')             if !defined $self->id;
    $self->action('')                   if !defined $self->action;
    $self->count_name('count')          if !defined $self->count_name;
    $self->count_id('repeatable_count') if !defined $self->count_id;
    $self->link_label('Add Another')    if !defined $self->link_label;

    $self->link_attrs->{href} = "" if !exists $self->link_attrs->{href};
    
    my $id = $self->id;
    
    $self->link_attrs->{onclick} = 
        "formfu_dojo_repeatable_${id}_get_data(); return false;"
        if !exists $self->link_attrs->{onclick};
        
    # save any elements already added
    my $save = $self->_elements;
    
    # add our custom elements
    $self->_elements([]);
    
    $self->element('Hidden')
        ->name( $self->count_name )
        ->default(0)
        ->id( $self->count_id );
    
    $self->element('Block')
        ->tag('script')
        ->attrs({ type => 'text/javascript' });
    
    # copy them to _custom_elements
    $self->_custom_elements( $self->_elements );
    
    # and restore any originals
    $self->_elements($save);

    return $self;
}

# these are overridden, to also return our custom elements

sub get_elements {
    my $self = shift;
    my %args = _parse_args(@_);

    my @elements = ( @{ $self->_elements }, @{ $self->_custom_elements } );

    return _get_elements( \%args, \@elements );
}

sub get_all_elements {
    my $self = shift;
    my %args = _parse_args(@_);

    my @e = map { $_, @{ $_->get_all_elements } } 
            ( @{ $self->_elements }, @{ $self->_custom_elements } );

    return _get_elements( \%args, \@e );
}

sub get_fields {
    my $self = shift;
    my %args = _parse_args(@_);

    my @e = map { $_->is_field ? $_ : @{ $_->get_fields } } 
            ( @{ $self->_elements }, @{ $self->_custom_elements } );

    return _get_elements( \%args, \@e );
}

###

sub repeat {
    my ( $self ) = @_;
    
    my $count = $self->count
        or return;
    
    my @children = map { $_->clone } @{ $self->_elements };
    
    return if !@children;
    
    for my $rep_count ( 1 .. $count ) {
        my @repeat;
        
        for my $child_count ( 1 .. @children ) {
            my $child = $children[$child_count-1];
            my $clone = $child->clone;
            my $last  = $self->_elements->[-1];
            
            if ( $child_count == 1 && defined $self->repeat_id ) {
                my $id = $self->repeat_id;
                $id =~ s/%c/$rep_count/g;
                $clone->id($id);
            }
            
            $self->insert_after( $clone, $last );
            push @repeat, $clone;
            
            for my $elem ( $clone, @{ $clone->get_all_elements } ) {
                if ( $elem->is_field ) {
                    my $name = $elem->name;
                    
                    if ( $self->increment_name && $name =~ /0/ ) {
                        $name =~ s/0/$rep_count/e;
                        $elem->name( $name );
                    }
                    
                    next if !$self->form->query;
                    
                    my $value = $self->form->param($name);
                    
                    if ( defined $value ) {
                        $value = $elem->process_value($value);
                        $self->form->add_valid( $name => $value );
                    }
                }
            }
        }
        push @{ $self->_repeated }, \@repeat;
    }
    
    $self->count(0);
    
    return;
}

sub last_repeat {
    my ($self) = @_;
    
    return map { @$_ } $self->_repeated->[-1];
}

sub all_repeats {
    my ($self) = @_;
    
    return map { @$_ } @{ $self->_repeated };
}

sub process {
    my ($self) = @_;
    
    my ($count) = $self->form->query->param( $self->count_name );
    
    $self->count($count);
    $self->repeat;
    
    return;
}

sub render {
    my $self = shift;
    
    my ($add_button) = grep {
        $_->type eq 'Block' && $_->tag eq 'script'
    } @{ $self->_custom_elements };
    
    my $id = $self->id;
    my $action     = $self->action;
    my $link_label = xml_escape( $self->link_label );
    my $count_id   = $self->count_id;
    my $link_attrs = process_attrs( $self->link_attrs );
    
    my $js = <<EOF;
dojo.require("dojo.dom");
dojo.require("dojo.html.common");
dojo.require("dojo.html.util"); // for createNodesFromText()

function formfu_dojo_repeatable_${id}_get_data () {
	var count = dojo.byId('$count_id');
	var value = ++count.value;
	var bindArgs = {
		url:  '$action',
		method: "POST",
		preventCache: 1,
		content: { count: value },
		load: function(type, data, evt){
			var html = dojo.html.createNodesFromText(data);
			for (x in html) {
				dojo.dom.insertBefore( html[x], count );
			}
		}
	};
	dojo.io.bind(bindArgs);
}

if (dojo) {
    document.write('<a $link_attrs>$link_label</a>');
}
EOF
    
    $add_button->content_xml($js);
    
    # save for restoration later
    my $elem   = $self->_elements;
    my $custom = $self->_custom_elements;

    $self->repeat;
    
    $self->_elements([
        @{ $elem },
        @{ $custom },
        ]);
    
    my $render = $self->SUPER::render({
        @_ ? %{$_[0]} : ()
        });

    append_xml_attribute( $render->{attributes},
        'class', 'repeatable' );
    
    # restore
    $self->_elements( $elem );
    $self->_custom_elements( $custom );
    
    return $render;
}

1;

__END__

=head1 NAME

HTML::FormFu::Element::Dojo::Repeatable

=head1 SYNOPSIS

    ---
    elements:
      - type: Dojo::Repeatable
        action: /callback-url
        elements:
          - type: text
            name: foo
            label: Foo
          - type: text
            name: bar
            label: Bar

=head1 DESCRIPTION

Provides a link which uses a Dojo/AJAX callback to the webserver to 
insert into the form a repetition of a field (or any element or number of 
elements).

The repeated fields may all have the same name, which would submit multiple 
values, or if L</increment_name> is true, and the field name contains a 
C<0>, then each new field will have an incremented name.

=head1 METHODS

=head2 action

Arguments: $url

The callback url which returns the markup for repeated elements.

=head2 count

Arguments: $count

Set the number of times the contents of the repeatable element should be 
duplicated.

=head2 repeat_id

Arguments: $id

Sets the id attribute of the first child element of each repeated duplicate. 
Any occurance of the string C<%c> will be substituted with the L</count> 
number.

B<SUBJECT TO CHANGE>. Currently, the L</repeat_id> is not applied to the 
original contents of the repeatable element, so you would have to set this 
explicitly, like so:

    ---
    element: 
        type: Dojo::Repeatable
        repeat_id: foo%c
        elements: 
          - type: text
            name: foo
            id: foo0

This would set the first text field's id to C<foo0>, and repeated duplicates 
would get the id's C<foo1>, C<foo2>, etc.

=head2 count_name

Arguments: $name



=head2 link_label

Arguments: $label

Set the text of the C<< <a> >> link which triggers the repeating action.

=head2 link_attributes

=head2 link_attrs

Arguments: [%attributes]

Arguments: [\%attributes]

Return Value: $element

Accepts either a list of key/value pairs, or a hash-ref.

    ---
    link_attributes:
      id: repeat_link
      class: repeatable_link

As a special case, if no arguments are passed, the attributes hash-ref is 
returned. This allows the following idioms.

    # set a value
    $element->link_attributes->{id} = 'repeat_link';
    
    # delete all attributes
    %{ $element->link_attributes } = ();

L</link_attrs> is an alias for L</link_attributes>. 

=head1 SEE ALSO

Is a sub-class of, and inherits methods from L<HTML::FormFu::Element::Block>, 
L<HTML::FormFu::Element>

L<HTML::FormFu>, L<Catalyst::Controller::HTML::FormFu>, L<HTML::Dojo>

=head1 SUPPORT, BUGS, ETC

See L<HTML::FormFu::Dojo>

=head1 AUTHOR

Carl Franks

=head1 LICENSE

This library is free software, you can redistribute it and/or modify it under
the same terms as Perl itself.
